package org.fujene.term;

import java.util.HashMap;
import java.util.Map;

/**
 * 文字列の最適化を実施するクラスです。主に全角<->半角文字の変換処理を行います。
 * 
 * @author yasuda
 */
public class Optimizer{

	/**
	 * 全角文字から半角文字への変換を行います。
	 * 全角文字：全角英数字[０-９ａ-ｚＡ-Ｚ]、全角記号、全角カナ
	 * 
	 * @param str
	 * @return
	 */
	public static String ZenToHan(String str){
		return ZenToHan(str, false, false);
	}

	/**
	 * 全角文字から半角文字への変換を行います。
	 * 全角文字：全角英数字[０-９ａ-ｚＡ-Ｚ]、全角記号、全角カナ
	 * 
	 * @param str
	 * @param lowerCase
	 * @param upperCase
	 * @return
	 */
	public static String ZenToHan(String str, boolean lowerCase, boolean upperCase){
		if (str == null) return null;

		str = ZenKanaToHan(str);
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			char nc = ZenNumberToHan(c);
			if (nc == c) nc = ZenAlphaToHan(c);
			if (nc == c) nc = ZenSymbolToHan(c);

			if (lowerCase) nc = Character.toLowerCase(nc);
			if (upperCase) nc = Character.toUpperCase(nc);

			builder.append(nc);
		}
		return builder.toString();
	}

	/**
	 * 半角文字から全角文字への変換を行います。
	 * 半角文字：半角英数字[0-9a-zA-Z]、半角記号、半角カナ
	 * 
	 * @param str
	 * @return
	 */
	public static String HanToZen(String str){

		return HanToZen(str, false, false);
	}

	/**
	 * 半角文字から全角文字への変換を行います。
	 * 半角文字：半角英数字[0-9a-zA-Z]、半角記号、半角カナ
	 * 
	 * @param str
	 * @param lowerCase
	 * @param upperCase
	 * @return
	 */
	public static String HanToZen(String str, boolean lowerCase, boolean upperCase){
		if (str == null) return null;

		str = HanKanaToZen(str);
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			char nc = HanNumberToZen(c);
			if (nc == c) nc = HanAlphaToZen(c);
			if (nc == c) nc = HanSymbolToZen(c);

			if (lowerCase) nc = Character.toLowerCase(nc);
			if (upperCase) nc = Character.toUpperCase(nc);
			builder.append(nc);
		}
		return builder.toString();
	}

	/**
	 * 半角カナを全角カナに変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String HanKanaToZen(String str){
		if (str == null) return null;
		char[] ary = str.toCharArray();
		StringBuilder builder = new StringBuilder();
		for(int i = 0; i < ary.length; i++){
			if (i > 0 && (ary[i] == 'ﾞ' || ary[i] == 'ﾟ')){
				String complex = String.format("%s%s", ary[i - 1], ary[i]);
				if (hankanaSonatMap.containsKey(complex)){
					builder.deleteCharAt(builder.length() - 1);
					builder.append(hankanaSonatMap.get(complex));
					continue;
				}
			}

			if (hankanaMap.containsKey(ary[i])){
				builder.append(hankanaMap.get(ary[i]));
			}else{
				builder.append(ary[i]);
			}

		}
		return builder.toString();
	}

	/**
	 * 全角カナを半角カナに変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String ZenKanaToHan(String str){
		if (str == null) return null;
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			if (zenkanaMap.containsKey(c)){
				builder.append(zenkanaMap.get(c));
			}else{
				builder.append(c);
			}

		}
		return builder.toString();
	}

	/**
	 * 半角数字を全角数字に変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String HanNumberToZen(String str){
		if (str == null) return null;
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			if (0x30 <= c && c <= 0x39){
				c = (char)(c + 0xfee0);
			}
			builder.append(c);
		}
		return builder.toString();
	}

	/**
	 * 半角数字を全角数字に変換します。
	 * 
	 * @param c
	 * @return
	 */
	public static char HanNumberToZen(char c){
		if (0x30 <= c && c <= 0x39){
			c = (char)(c + 0xfee0);
		}
		return c;
	}

	/**
	 * 半角数字を全角数字に変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String ZenNumberToHan(String str){
		if (str == null) return null;
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			builder.append(ZenNumberToHan(c));
		}
		return builder.toString();
	}

	/**
	 * 半角数字を全角数字に変換します。
	 * 
	 * @param c
	 * @return
	 */
	public static char ZenNumberToHan(char c){
		if (0xff10 <= c && c <= 0xff19){
			c = (char)(c - 0xfee0);
		}
		return c;
	}

	/**
	 * 半角アルファベットを全角に変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String HanAlphaToZen(String str){
		return HanAlphaToZen(str, false, false);
	}

	/**
	 * 半角アルファベットを全角に変換します。
	 * 
	 * @param str
	 * @param lowerCase
	 * @param upperCase
	 * @return
	 */
	public static String HanAlphaToZen(String str, boolean lowerCase, boolean upperCase){
		if (str == null) return null;
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			c = HanAlphaToZen(c);
			if (lowerCase) c = Character.toLowerCase(c);
			if (upperCase) c = Character.toUpperCase(c);
			builder.append(c);
		}
		return builder.toString();
	}

	/**
	 * 半角アルファベットを全角に変換します。
	 * 
	 * @param c
	 * @return
	 */
	public static char HanAlphaToZen(char c){
		if ((0x41 <= c && c <= 0x5a) || (0x61 <= c && c <= 0x7a)){
			c = (char)(c + 0xfee0);
		}
		return c;
	}

	/**
	 * 全角アルファベットを半角に変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String ZenAlphaToHan(String str){
		return ZenAlphaToHan(str, false, false);
	}

	/**
	 * 全角アルファベットを半角に変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String ZenAlphaToHan(String str, boolean lowerCase, boolean upperCase){
		if (str == null) return null;
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			c = ZenAlphaToHan(c);
			if (lowerCase) c = Character.toLowerCase(c);
			if (upperCase) c = Character.toUpperCase(c);
			builder.append(c);
		}
		return builder.toString();
	}

	/**
	 * 全角アルファベットを半角に変換します。
	 * 
	 * @param c
	 * @return
	 */
	public static char ZenAlphaToHan(char c){
		if ((0xff21 <= c && c <= 0xff3a) || (0xff41 <= c && c <= 0xff5a)){
			c = (char)(c - 0xfee0);
		}
		return c;
	}

	/**
	 * 半角記号を全角記号に変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String HanSymbolToZen(String str){
		if (str == null) return null;
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			c = HanSymbolToZen(c);
			builder.append(c);
		}
		return builder.toString();
	}

	/**
	 * 半角記号を全角記号に変換します。
	 * 
	 * @param c
	 * @return
	 */
	public static char HanSymbolToZen(char c){
		if (hanasciiMap.containsKey(c)){
			return hanasciiMap.get(c);
		}else{
			return c;
		}
	}

	/**
	 * 全角記号を半角記号に変換します。
	 * 
	 * @param str
	 * @return
	 */
	public static String ZenSymbolToHan(String str){
		if (str == null) return null;
		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			c = ZenSymbolToHan(c);
			builder.append(c);
		}
		return builder.toString();
	}

	/**
	 * 全角記号を半角記号に変換します。
	 * 
	 * @param c
	 * @return
	 */
	public static char ZenSymbolToHan(char c){
		if (zenasciiMap.containsKey(c)){
			return zenasciiMap.get(c);
		}else{
			return c;
		}
	}

	/**
	 * 渡された文字が数値かどうかを判定します。
	 * 
	 * @param c
	 * @return
	 */
	public static boolean isNumber(String str){
		if (str == null) return false;
		boolean isOnlyNumber = true;
		for(char c: str.toCharArray()){
			if (!isNumber(c)){
				isOnlyNumber = false;
				break;
			}
		}
		return isOnlyNumber;
	}

	/**
	 * 渡された文字が数値かどうかを判定します。
	 * 
	 * @param c
	 * @return
	 */
	public static boolean isNumber(char c){
		return (0xff10 <= c && c <= 0xff19) || (0x30 <= c && c <= 0x39);
	}

	/**
	 * 渡された文字列がASCII文字だけで構成されているかどうかを判定します。
	 * 
	 * @param str
	 * @return
	 */
	public static boolean isOnlyAscii(String str){
		if (str == null) return false;
		boolean isOnlyAscii = true;
		for(char c: str.toCharArray()){
			if (!isAscii(c)){
				isOnlyAscii = false;
				break;
			}
		}

		return isOnlyAscii;
	}

	/**
	 * 渡された文字列がASCII文字かどうかを判定します。
	 * 
	 * @param c
	 * @return
	 */
	public static boolean isAscii(char c){
		return c < 0x7d;
	}

	/**
	 * わたされた文字列がすべて仮名文字かどうかを判定します。
	 * 
	 * @param str
	 * @return
	 */
	public static boolean isOnlyKana(String str){
		for(char c: str.toCharArray()){
			if (!(c >= 0x3041 && c <= 0x3093) && !zenkanaMap.containsKey(c) && !hankanaMap.containsKey(c)){
				return false;
			}
		}
		return true;
	}

	/**
	 * ひらがな⇒カタカナへの変換
	 * 
	 * @param str
	 * @return
	 */
	public static String HiraToKata(String str){
		StringBuffer sb = new StringBuffer(str);
		for(int i = 0; i < sb.length(); i++){
			char c = sb.charAt(i);
			if (c >= 'ぁ' && c <= 'ゖ'){
				sb.setCharAt(i, (char)(c - 'ぁ' + 'ァ'));
			}
		}
		return sb.toString();
	}

	/**
	 * 半角記号->全角記号のマッピングテーブル
	 */
	private static Map<Character, Character> hanasciiMap = new HashMap<Character, Character>();
	static{
		hanasciiMap.put('!', '！');
		hanasciiMap.put('"', '”');
		hanasciiMap.put('#', '＃');
		hanasciiMap.put('$', '＄');
		hanasciiMap.put('%', '％');
		hanasciiMap.put('&', '＆');
		hanasciiMap.put('\'', '’');
		hanasciiMap.put('(', '（');
		hanasciiMap.put(')', '）');
		hanasciiMap.put('*', '＊');
		hanasciiMap.put('+', '＋');
		hanasciiMap.put(',', '，');
		hanasciiMap.put('-', '‐');
		hanasciiMap.put('.', '．');
		hanasciiMap.put('/', '／');
		hanasciiMap.put(':', '：');
		hanasciiMap.put(';', '；');
		hanasciiMap.put('<', '＜');
		hanasciiMap.put('=', '＝');
		hanasciiMap.put('>', '＞');
		hanasciiMap.put('?', '？');
		hanasciiMap.put('@', '＠');
		hanasciiMap.put('[', '［');
		hanasciiMap.put('\\', '￥');
		hanasciiMap.put(']', '］');
		hanasciiMap.put('^', '＾');
		hanasciiMap.put('_', '＿');
		hanasciiMap.put('`', '｀');
		hanasciiMap.put('{', '｛');
		hanasciiMap.put('|', '｜');
		hanasciiMap.put('}', '｝');
		hanasciiMap.put('~', '～');

	}

	/**
	 * 全角記号->半角記号のマッピングテーブル
	 */
	private static Map<Character, Character> zenasciiMap = new HashMap<Character, Character>();
	static{
		zenasciiMap.put('！', '!');
		zenasciiMap.put('”', '"');
		zenasciiMap.put('＃', '#');
		zenasciiMap.put('＄', '$');
		zenasciiMap.put('％', '%');
		zenasciiMap.put('＆', '&');
		zenasciiMap.put('’', '\'');
		zenasciiMap.put('（', '(');
		zenasciiMap.put('）', ')');
		zenasciiMap.put('＊', '*');
		zenasciiMap.put('＋', '+');
		zenasciiMap.put(',', ',');
		zenasciiMap.put('‐', '-');
		zenasciiMap.put('．', '.');
		zenasciiMap.put('／', '/');
		zenasciiMap.put('：', ':');
		zenasciiMap.put('；', ';');
		zenasciiMap.put('＜', '<');
		zenasciiMap.put('＝', '=');
		zenasciiMap.put('＞', '>');
		zenasciiMap.put('？', '?');
		zenasciiMap.put('＠', '@');
		zenasciiMap.put('［', '[');
		zenasciiMap.put('￥', '\\');
		zenasciiMap.put('］', ']');
		zenasciiMap.put('＾', '^');
		zenasciiMap.put('＿', '_');
		zenasciiMap.put('｀', '`');
		zenasciiMap.put('｛', '{');
		zenasciiMap.put('｜', '|');
		zenasciiMap.put('｝', '}');
		zenasciiMap.put('～', '~');
	}

	/**
	 * 半角カナ->全角カナのマッピングテーブル（濁音／鼻濁音除く）
	 */
	private static Map<Character, Character> hankanaMap = new HashMap<Character, Character>();
	static{
		hankanaMap.put('ｱ', 'ア');
		hankanaMap.put('ｧ', 'ァ');
		hankanaMap.put('ｲ', 'イ');
		hankanaMap.put('ｨ', 'ィ');
		hankanaMap.put('ｳ', 'ウ');
		hankanaMap.put('ｩ', 'ゥ');
		hankanaMap.put('ｴ', 'エ');
		hankanaMap.put('ｪ', 'ェ');
		hankanaMap.put('ｵ', 'オ');
		hankanaMap.put('ｫ', 'ォ');
		hankanaMap.put('ｶ', 'カ');
		hankanaMap.put('ｷ', 'キ');
		hankanaMap.put('ｸ', 'ク');
		hankanaMap.put('ｹ', 'ケ');
		hankanaMap.put('ｺ', 'コ');
		hankanaMap.put('ｻ', 'サ');
		hankanaMap.put('ｼ', 'シ');
		hankanaMap.put('ｽ', 'ス');
		hankanaMap.put('ｾ', 'セ');
		hankanaMap.put('ｿ', 'ソ');
		hankanaMap.put('ﾀ', 'タ');
		hankanaMap.put('ﾁ', 'チ');
		hankanaMap.put('ﾂ', 'ツ');
		hankanaMap.put('ｯ', 'ッ');
		hankanaMap.put('ﾃ', 'テ');
		hankanaMap.put('ﾄ', 'ト');
		hankanaMap.put('ﾅ', 'ナ');
		hankanaMap.put('ﾆ', 'ニ');
		hankanaMap.put('ﾇ', 'ヌ');
		hankanaMap.put('ﾈ', 'ネ');
		hankanaMap.put('ﾉ', 'ノ');
		hankanaMap.put('ﾊ', 'ハ');
		hankanaMap.put('ﾋ', 'ヒ');
		hankanaMap.put('ﾌ', 'フ');
		hankanaMap.put('ﾍ', 'ヘ');
		hankanaMap.put('ﾎ', 'ホ');
		hankanaMap.put('ﾏ', 'マ');
		hankanaMap.put('ﾐ', 'ミ');
		hankanaMap.put('ﾑ', 'ム');
		hankanaMap.put('ﾒ', 'メ');
		hankanaMap.put('ﾓ', 'モ');
		hankanaMap.put('ﾔ', 'ヤ');
		hankanaMap.put('ｬ', 'ャ');
		hankanaMap.put('ﾕ', 'ユ');
		hankanaMap.put('ｭ', 'ュ');
		hankanaMap.put('ﾖ', 'ヨ');
		hankanaMap.put('ｮ', 'ョ');
		hankanaMap.put('ﾗ', 'ラ');
		hankanaMap.put('ﾘ', 'リ');
		hankanaMap.put('ﾙ', 'ル');
		hankanaMap.put('ﾚ', 'レ');
		hankanaMap.put('ﾛ', 'ロ');
		hankanaMap.put('ﾜ', 'ワ');
		hankanaMap.put('ｦ', 'ヲ');
		hankanaMap.put('ﾝ', 'ン');
		hankanaMap.put('ｰ', 'ー');
		hankanaMap.put('｡', '。');
		hankanaMap.put('､', '、');
		hankanaMap.put('ﾟ', '゜');
		hankanaMap.put('ﾞ', '゛');
	}

	/**
	 * 半角カナ->全角カナのマッピングテーブル（濁音、鼻濁音のみ）
	 */
	private static Map<String, String> hankanaSonatMap = new HashMap<String, String>();
	static{
		hankanaSonatMap.put("ｳﾞ", "ヴ");
		hankanaSonatMap.put("ｶﾞ", "ガ");
		hankanaSonatMap.put("ｷﾞ", "ギ");
		hankanaSonatMap.put("ｸﾞ", "グ");
		hankanaSonatMap.put("ｹﾞ", "ゲ");
		hankanaSonatMap.put("ｺﾞ", "ゴ");
		hankanaSonatMap.put("ｻﾞ", "ザ");
		hankanaSonatMap.put("ｼﾞ", "ジ");
		hankanaSonatMap.put("ｽﾞ", "ズ");
		hankanaSonatMap.put("ｾﾞ", "ゼ");
		hankanaSonatMap.put("ｿﾞ", "ゾ");
		hankanaSonatMap.put("ﾀﾞ", "ダ");
		hankanaSonatMap.put("ﾁﾞ", "ヂ");
		hankanaSonatMap.put("ﾂﾞ", "ヅ");
		hankanaSonatMap.put("ﾃﾞ", "デ");
		hankanaSonatMap.put("ﾄﾞ", "ド");
		hankanaSonatMap.put("ﾊﾞ", "バ");
		hankanaSonatMap.put("ﾋﾞ", "ビ");
		hankanaSonatMap.put("ﾌﾞ", "ブ");
		hankanaSonatMap.put("ﾍﾞ", "ベ");
		hankanaSonatMap.put("ﾎﾞ", "ボ");
		hankanaSonatMap.put("ﾊﾟ", "パ");
		hankanaSonatMap.put("ﾋﾟ", "ピ");
		hankanaSonatMap.put("ﾌﾟ", "プ");
		hankanaSonatMap.put("ﾍﾟ", "ペ");
		hankanaSonatMap.put("ﾎﾟ", "ポ");
	}

	/**
	 * 全角カナ->半角カナのマッピングテーブル
	 */
	private static Map<Character, String> zenkanaMap = new HashMap<Character, String>();
	static{
		zenkanaMap.put('ア', "ｱ");
		zenkanaMap.put('ァ', "ｧ");
		zenkanaMap.put('イ', "ｲ");
		zenkanaMap.put('ィ', "ｨ");
		zenkanaMap.put('ウ', "ｳ");
		zenkanaMap.put('ゥ', "ｩ");
		zenkanaMap.put('エ', "ｴ");
		zenkanaMap.put('ェ', "ｪ");
		zenkanaMap.put('オ', "ｵ");
		zenkanaMap.put('ォ', "ｫ");
		zenkanaMap.put('カ', "ｶ");
		zenkanaMap.put('キ', "ｷ");
		zenkanaMap.put('ク', "ｸ");
		zenkanaMap.put('ケ', "ｹ");
		zenkanaMap.put('コ', "ｺ");
		zenkanaMap.put('サ', "ｻ");
		zenkanaMap.put('シ', "ｼ");
		zenkanaMap.put('ス', "ｽ");
		zenkanaMap.put('セ', "ｾ");
		zenkanaMap.put('ソ', "ｿ");
		zenkanaMap.put('タ', "ﾀ");
		zenkanaMap.put('チ', "ﾁ");
		zenkanaMap.put('ツ', "ﾂ");
		zenkanaMap.put('ッ', "ｯ");
		zenkanaMap.put('テ', "ﾃ");
		zenkanaMap.put('ト', "ﾄ");
		zenkanaMap.put('ナ', "ﾅ");
		zenkanaMap.put('ニ', "ﾆ");
		zenkanaMap.put('ヌ', "ﾇ");
		zenkanaMap.put('ネ', "ﾈ");
		zenkanaMap.put('ノ', "ﾉ");
		zenkanaMap.put('ハ', "ﾊ");
		zenkanaMap.put('ヒ', "ﾋ");
		zenkanaMap.put('フ', "ﾌ");
		zenkanaMap.put('ヘ', "ﾍ");
		zenkanaMap.put('ホ', "ﾎ");
		zenkanaMap.put('マ', "ﾏ");
		zenkanaMap.put('ミ', "ﾐ");
		zenkanaMap.put('ム', "ﾑ");
		zenkanaMap.put('メ', "ﾒ");
		zenkanaMap.put('モ', "ﾓ");
		zenkanaMap.put('ヤ', "ﾔ");
		zenkanaMap.put('ャ', "ｬ");
		zenkanaMap.put('ユ', "ﾕ");
		zenkanaMap.put('ュ', "ｭ");
		zenkanaMap.put('ヨ', "ﾖ");
		zenkanaMap.put('ョ', "ｮ");
		zenkanaMap.put('ラ', "ﾗ");
		zenkanaMap.put('リ', "ﾘ");
		zenkanaMap.put('ル', "ﾙ");
		zenkanaMap.put('レ', "ﾚ");
		zenkanaMap.put('ロ', "ﾛ");
		zenkanaMap.put('ワ', "ﾜ");
		zenkanaMap.put('ヲ', "ｦ");
		zenkanaMap.put('ン', "ﾝ");
		zenkanaMap.put('ー', "ｰ");
		zenkanaMap.put('。', "｡");
		zenkanaMap.put('、', "､");
		zenkanaMap.put('゜', "ﾟ");
		zenkanaMap.put('゛', "ﾞ");
		zenkanaMap.put('ヴ', "ｳﾞ");
		zenkanaMap.put('ガ', "ｶﾞ");
		zenkanaMap.put('ギ', "ｷﾞ");
		zenkanaMap.put('グ', "ｸﾞ");
		zenkanaMap.put('ゲ', "ｹﾞ");
		zenkanaMap.put('ゴ', "ｺﾞ");
		zenkanaMap.put('ザ', "ｻﾞ");
		zenkanaMap.put('ジ', "ｼﾞ");
		zenkanaMap.put('ズ', "ｽﾞ");
		zenkanaMap.put('ゼ', "ｾﾞ");
		zenkanaMap.put('ゾ', "ｿﾞ");
		zenkanaMap.put('ダ', "ﾀﾞ");
		zenkanaMap.put('ヂ', "ﾁﾞ");
		zenkanaMap.put('ヅ', "ﾂﾞ");
		zenkanaMap.put('デ', "ﾃﾞ");
		zenkanaMap.put('ド', "ﾄﾞ");
		zenkanaMap.put('バ', "ﾊﾞ");
		zenkanaMap.put('ビ', "ﾋﾞ");
		zenkanaMap.put('ブ', "ﾌﾞ");
		zenkanaMap.put('ベ', "ﾍﾞ");
		zenkanaMap.put('ボ', "ﾎﾞ");
		zenkanaMap.put('パ', "ﾊﾟ");
		zenkanaMap.put('ピ', "ﾋﾟ");
		zenkanaMap.put('プ', "ﾌﾟ");
		zenkanaMap.put('ペ', "ﾍﾟ");
		zenkanaMap.put('ポ', "ﾎﾟ");
	}
}
